package com.gomoku.service;

import com.gomoku.model.Board;
import com.gomoku.model.Move;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class AIService_original {
    private static final int MAX_DEPTH = 4;
    private static final int WIN_SCORE = 100000;
    private static final int FIVE = 100000;
    private static final int OPEN_FOUR = 10000;
    private static final int FOUR = 1000;
    private static final int OPEN_THREE = 1000;
    private static final int THREE = 100;
    private static final int OPEN_TWO = 100;
    private static final int TWO = 10;

    public Move calculateNextMove(Board board) {
        return minimax(board, MAX_DEPTH, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
    }

    private Move minimax(Board board, int depth, int alpha, int beta, boolean isMaximizing) {
        if (depth == 0) {
            return new Move(-1, -1, evaluateBoard(board));
        }

        List<Move> availableMoves = generateMoves(board);
        if (availableMoves.isEmpty()) {
            return new Move(-1, -1, evaluateBoard(board));
        }

        Move bestMove = null;
        if (isMaximizing) {
            int maxEval = Integer.MIN_VALUE;
            for (Move move : availableMoves) {
                Board newBoard = board.copy();
                newBoard.makeMove(move.getRow(), move.getCol(), true); // AI move

                if (newBoard.checkWin(move.getRow(), move.getCol())) {
                    return new Move(move.getRow(), move.getCol(), WIN_SCORE);
                }

                int eval = minimax(newBoard, depth - 1, alpha, beta, false).getScore();
                if (eval > maxEval) {
                    maxEval = eval;
                    bestMove = new Move(move.getRow(), move.getCol(), eval);
                }
                alpha = Math.max(alpha, eval);
                if (beta <= alpha) {
                    break;
                }
            }
        } else {
            int minEval = Integer.MAX_VALUE;
            for (Move move : availableMoves) {
                Board newBoard = board.copy();
                newBoard.makeMove(move.getRow(), move.getCol(), false); // Player move

                if (newBoard.checkWin(move.getRow(), move.getCol())) {
                    return new Move(move.getRow(), move.getCol(), -WIN_SCORE);
                }

                int eval = minimax(newBoard, depth - 1, alpha, beta, true).getScore();
                if (eval < minEval) {
                    minEval = eval;
                    bestMove = new Move(move.getRow(), move.getCol(), eval);
                }
                beta = Math.min(beta, eval);
                if (beta <= alpha) {
                    break;
                }
            }
        }
        return bestMove;
    }

    private List<Move> generateMoves(Board board) {
        List<Move> moves = new ArrayList<>();
        int[][] grid = board.getGrid();

        // Only consider moves adjacent to existing pieces
        for (int i = 0; i < Board.BOARD_SIZE; i++) {
            for (int j = 0; j < Board.BOARD_SIZE; j++) {
                if (grid[i][j] == 0 && hasNeighbor(grid, i, j)) {
                    moves.add(new Move(i, j));
                }
            }
        }
        return moves;
    }

    private boolean hasNeighbor(int[][] grid, int row, int col) {
        for (int i = Math.max(0, row - 2); i <= Math.min(Board.BOARD_SIZE - 1, row + 2); i++) {
            for (int j = Math.max(0, col - 2); j <= Math.min(Board.BOARD_SIZE - 1, col + 2); j++) {
                if (grid[i][j] != 0) {
                    return true;
                }
            }
        }
        return false;
    }

    private int evaluateBoard(Board board) {
        int score = 0;
        int[][] grid = board.getGrid();

        // Evaluate horizontal, vertical, and diagonal lines
        for (int i = 0; i < Board.BOARD_SIZE; i++) {
            for (int j = 0; j < Board.BOARD_SIZE; j++) {
                if (grid[i][j] != 0) {
                    score += evaluateDirection(grid, i, j, 1, 0);  // horizontal
                    score += evaluateDirection(grid, i, j, 0, 1);  // vertical
                    score += evaluateDirection(grid, i, j, 1, 1);  // diagonal
                    score += evaluateDirection(grid, i, j, 1, -1); // anti-diagonal
                }
            }
        }
        return score;
    }



    private int evaluateDirection(int[][] grid, int row, int col, int dRow, int dCol) {
        int player = grid[row][col];
        int count = 1;
        int openEnds = 0;
        
        // Count forward
        int r = row + dRow;
        int c = col + dCol;
        while (isValidPosition(r, c) && grid[r][c] == player) {
            count++;
            r += dRow;
            c += dCol;
        }
        if (isValidPosition(r, c) && grid[r][c] == 0) openEnds++;
        
        // Count backward
        r = row - dRow;
        c = col - dCol;
        while (isValidPosition(r, c) && grid[r][c] == player) {
            count++;
            r -= dRow;
            c -= dCol;
        }
        if (isValidPosition(r, c) && grid[r][c] == 0) openEnds++;

        return calculateScore(count, openEnds, player == 2);
    }

    private boolean isValidPosition(int row, int col) {
        return row >= 0 && row < Board.BOARD_SIZE && col >= 0 && col < Board.BOARD_SIZE;
    }

    private int calculateScore(int count, int openEnds, boolean isAI) {
        int score = 0;
        if (count >= 5) score = FIVE;
        else if (count == 4) {
            if (openEnds == 2) score = OPEN_FOUR;
            else if (openEnds == 1) score = FOUR;
        }
        else if (count == 3) {
            if (openEnds == 2) score = OPEN_THREE;
            else if (openEnds == 1) score = THREE;
        }
        else if (count == 2) {
            if (openEnds == 2) score = OPEN_TWO;
            else if (openEnds == 1) score = TWO;
        }
        return isAI ? score : -score;
    }
}